﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using VPLink2013.Framework;

namespace VPLink2013.Batch
{
    public struct Coord
    {
        int x;
        int y;
        public Coord(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public override bool Equals(object ob)
        {
            if (!(ob is Coord))
            {
                return false;
            }
            else
            {
                Coord obCoord = (Coord)ob;
                if (obCoord.x == this.x && obCoord.y == this.y)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }
    public static class InsertionManager
    {
        #region Helpers Methods

        private static List<Coord> GetFixedCoord(List<XElement> employees)
        {
            List<Coord> fixedCoord = new List<Coord>();
            foreach (XElement elem in employees)
            {
                if (elem.isAttributeValue("Fixed", "True"))
                {
                    int x = Int32.Parse(elem.Attribute("RowNumber").Value);
                    int y = Int32.Parse(elem.Attribute("ColumnNumber").Value);
                    fixedCoord.Add(new Coord(x, y));
                }
            }
            return fixedCoord;
        }

        /// <summary>
        /// Compare 2 employees from service
        /// </summary>
        /// <param name="parentService"></param>
        /// <param name="e1"></param>
        /// <param name="e2"></param>
        /// <returns></returns>
        private static int CompareOverAll(this XElement parentService, XElement e1, XElement e2)
        {
            if (e1 == null && e2 == null)
            {
                return 0;
            }
            else
            {
                if (e1 == null) return 1;
                else
                    if (e2 == null)
                    {
                        return -1;
                    }
                    else
                    {
                        int ret = 0;
                        int levelE1 = Int32.Parse(e1.Attribute("Level").Value);
                        int levelE2 = Int32.Parse(e2.Attribute("Level").Value);
                        bool e1IsManager = e1.Attribute("IsManager") != null ? !string.IsNullOrEmpty(e1.Attribute("IsManager").Value) ? bool.Parse(e1.Attribute("IsManager").Value) : false : false;
                        bool e2IsManager = e2.Attribute("IsManager") != null ? !string.IsNullOrEmpty(e2.Attribute("IsManager").Value) ? bool.Parse(e2.Attribute("IsManager").Value) : false : false;
                        string e1ManagerAccount = e1.Attribute("Manager") != null ? e1.Attribute("Manager").Value : string.Empty;
                        string e2ManagerAccount = e2.Attribute("Manager") != null ? e2.Attribute("Manager").Value : string.Empty;
                        string e1AccountName = e1.Attribute("LoginUser").Value;
                        string e2AccountName = e2.Attribute("LoginUser").Value;

                        XElement e1Manager = (!string.IsNullOrEmpty(e1ManagerAccount)
                            ? parentService.GetUniqueElementByAttributes("User", new Dictionary<string, string>() { 
							{ "LoginUser", e1ManagerAccount},
 							{ "IsManager", "True" }
						}) : null);
                        XElement e2Manager = (!string.IsNullOrEmpty(e2ManagerAccount)
                            ? parentService.GetUniqueElementByAttributes("User", new Dictionary<string, string>() { 
							{ "LoginUser", e2ManagerAccount},
 							{ "IsManager", "True" }
						}) : null);

                        // If employees are eachother managers
                        if (e1ManagerAccount == e2AccountName && e2ManagerAccount == e1AccountName)
                        {
                            return 0;
                        }
                        else
                        {
                            // if e2 is e1 Manager
                            if (e1ManagerAccount == e2AccountName)
                            {
                                ret = 1;
                            }
                            else
                            {
                                // if e1 is e2 Manager 
                                if (e2ManagerAccount == e1AccountName)
                                {
                                    ret = -1;
                                }
                                else
                                {
                                    ret = (levelE1 == levelE2)
                                        // If same level
                                                ? (e1IsManager == e2IsManager)
                                        // if both are manger
                                                    ? parentService.CompareOverAll(e1Manager, e2Manager) == 0
                                        // if same manager
                                        // compare names
                                                        ? CompareByName(e1, e2)
                                        // if not same manager
                                        // compare managers
                                                    : parentService.CompareOverAll(e1Manager, e2Manager)
                                        // if one is not manager 
                                                : parentService.CompareOverAll(e1Manager, e2Manager) == 0
                                                ? e1IsManager ? 1 : -1
                                                : parentService.CompareOverAll(e1Manager, e2Manager)
                                        // if not same level
                                        // if both are managers or both are employees
                                            : (e1IsManager == e2IsManager)
                                        // if both are managers, compare their managerss
                                                ? e1IsManager
                                                    ? (levelE1 > levelE2)
                                                            ? parentService.CompareOverAll(e1Manager, e2)
                                                            : parentService.CompareOverAll(e1, e2Manager)
                                        // if both employees, compare level
                                        //: levelE1 - levelE2
                                                : parentService.CompareOverAll(e1Manager, e2Manager)
                                        // if both are not managers/employees
                                            : e1IsManager
                                                ? parentService.CompareOverAll(e1, e2Manager)
                                                : parentService.CompareOverAll(e1Manager, e2);
                                }
                            }
                            return ret;
                        }
                    }
            }
        }
        private static int CompareByName(XElement e1, XElement e2)
        {
            if (string.Compare(e1.Attribute("FirstName").Value, e2.Attribute("FirstName").Value) == 0)
            {
                return string.Compare(e1.Attribute("LastName").Value, e2.Attribute("LastName").Value);
            }
            else
            {
                return string.Compare(e1.Attribute("FirstName").Value, e2.Attribute("FirstName").Value);
            }
        }

        private static bool IsSortable(this XElement parentService, XElement emp)
        {
            if (emp.isAttributeValue("IsManager", "true") && !emp.isAttributeValue("Visible", "False"))
            {
                return true;
            }
            else
            {
                string managerAccount = emp.Attribute("Manager").Value;
                XElement eManager = (!string.IsNullOrEmpty(managerAccount)
                        ? parentService.GetUniqueElementByAttributes("User",
                        new Dictionary<string, string>() { 
							{ "LoginUser", managerAccount }
						}) : null);
                if (eManager != null && !emp.isAttributeValue("Visible", "False"))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        #endregion

        public static void CalculateEmployeesCoord(this XElement parentService, List<XElement> allEmployees, int colNum)
        {

            // Get only managers and employees with Manager present in same level
            Console.WriteLine("Comparaison des employee de {0}", parentService.Attribute("Name").Value);
            List<XElement> sortableEmployees = allEmployees.Where(x => parentService.IsSortable(x)).ToList();
            List<XElement> notSortableEmployees = allEmployees.Where(x => !parentService.IsSortable(x)).ToList();
            string serviceCode = parentService.Attribute("Code").Value;
            try
            {
                sortableEmployees.Sort((x, y) => parentService.CompareOverAll(x, y));
                //sortableEmployees.Sort( ( x, y ) => parentService.CompareOverAll( x, y ) );
            }
            catch (Exception ex)
            {
                ContextTrace.AddTrace(string.Format("Exception sort : {0}", ex.Message));
            }

            int serviceLevel = Int32.Parse(parentService.Attribute("ServiceLevel").Value);
            int row; int col;
            bool incAfterManager; bool firstInserted;

            try
            {
                // Set coord to employees and Managers
                SetCoordToSortableEmployees(colNum, sortableEmployees, serviceLevel, serviceCode, out row, out col, out incAfterManager, out firstInserted);

                // Set coord to employees without Managers
                if (notSortableEmployees != null)
                {
                    if (notSortableEmployees.Count > 0)
                    {
                        SetCoordToNotSortableEmployees(parentService, notSortableEmployees, colNum, ref row, ref col, ref incAfterManager, firstInserted);
                    }
                }
            }
            catch (Exception ex)
            {
                ContextTrace.AddTrace(string.Format("Exception setCoord : {0}", ex.Message));
            }
        }

        private static void SetCoordToNotSortableEmployees(XElement parentService, List<XElement> notSortableEmployees, int colNum,
            ref int row,
            ref int col,
            ref bool incAfterManager,
            bool firstInserted)
        {
            if (firstInserted && !incAfterManager)
            {
                col = 0;
                row++;
            }
            notSortableEmployees.Sort((x, y) => CompareByName(x, y));

            notSortableEmployees[0].SetAttributeValue("UnrelatedEmplyee", "True");
            foreach (XElement emp in notSortableEmployees)
            {
                emp.SetAttributeValue("RowNumber", row);
                emp.SetAttributeValue("ColumnNumber", col);
                MoveNextColumn(colNum, ref row, ref col, ref incAfterManager);
            }
        }

        private static void SetCoordToSortableEmployees(int colNum, List<XElement> sortableEmployees, int serviceLevel, string serviceCode,
            out int row,
            out int col,
            out bool incAfterManager,
            out bool firstInserted)
        {
            row = 0;
            col = 0;
            incAfterManager = false;
            firstInserted = false;
            int previousLevel = 0;
            bool previousEmplIsManager = false;
            string lastManager = string.Empty;

            List<Coord> fixedCoord = GetFixedCoord(sortableEmployees);

            foreach (XElement emp in sortableEmployees)
            {
                int empServiceLevel = Int32.Parse(emp.Attribute("ServiceLevel").Value);
                int emplLevel = Int32.Parse(emp.Attribute("Level").Value);

                if (!emp.isAttributeValue("Fixed", "True"))
                {
                    StepsBeforeSetCoord(sortableEmployees, serviceLevel, serviceCode, ref row, ref col, incAfterManager, firstInserted, previousLevel, previousEmplIsManager, lastManager, emp, empServiceLevel, emplLevel);
                    int xCoord = row; int yCoord = col;
                    bool found = fixedCoord.Any(x => x.Equals(new Coord(xCoord, yCoord)));

                    while (found)
                    {
                        StepsBeforeSetCoord(sortableEmployees, serviceLevel, serviceCode, ref row, ref col, incAfterManager, firstInserted, previousLevel, previousEmplIsManager, lastManager, emp, empServiceLevel, emplLevel);
                        xCoord = row; yCoord = col;
                        // FIX TO TRY => si la première personne (0,0) n'est pas paramétrer et la personne paramétré (0,0)
                        //found = firstInserted && fixedCoord.Any( x => x.Equals( new Coord( xCoord, yCoord ) ) );
                        found = fixedCoord.Any(x => x.Equals(new Coord(xCoord, yCoord)));
                    }
                    emp.SetAttributeValue("RowNumber", row);
                    emp.SetAttributeValue("ColumnNumber", col);

                    MoveNextColumn(colNum, ref row, ref col, ref incAfterManager);
                }
                firstInserted = true;
                previousEmplIsManager = emp.isAttributeValue("IsManager", "True");

                if (previousEmplIsManager)
                    lastManager = emp.Attribute("LoginUser").Value;

                previousLevel = emplLevel;
            }
        }

        private static void StepsBeforeSetCoord(List<XElement> sortableEmployees, int serviceLevel, string serviceCode,
            ref int row,
            ref int col,
            bool incAfterManager, bool firstInserted, int previousLevel,
            bool previousEmplIsManager, string lastManager,
            XElement emp, int empServiceLevel, int emplLevel)
        {
            if (emp.isAttributeValue("IsManager", "True"))
            {
                string managerAccount = emp.Attribute("LoginUser").Value;
                string ownManager = emp.Attribute("Manager").Value;
                bool managerHasEmployeesInSameLevel = sortableEmployees
                    .Any(x => managerAccount.Equals(x.Attribute("Manager").Value, StringComparison.CurrentCultureIgnoreCase));

                if (!incAfterManager && firstInserted)
                {
                    // Stay on same row if no employees and last manager is your manager
                    if ((!managerHasEmployeesInSameLevel
                        && ownManager.Equals(lastManager) && serviceLevel < 3)
                        // Exception RH 
                        //	&& !( serviceCode.Equals( "DIR - DRH", StringComparison.CurrentCultureIgnoreCase ) && emplLevel == 4 ) )
                        || (!previousEmplIsManager
                        // Exception RH 
                            && !(serviceCode.Equals("DIR - DRH", StringComparison.CurrentCultureIgnoreCase) && emplLevel == 4))
                        || (previousLevel != emplLevel && previousEmplIsManager
                        // Exception DF
                            && !(serviceCode.Equals("DPT - DF", StringComparison.CurrentCultureIgnoreCase) && emplLevel == 5))
                        || managerHasEmployeesInSameLevel)
                    {
                        row++;
                        col = 0;
                    }
                }
            }
        }

        private static void MoveNextColumn(int colNum, ref int row, ref int col, ref bool incAfterManager)
        {
            // If Last column is reach, go to the next row
            if (col + 1 > colNum)
            {
                row++;
                col = 0;
                incAfterManager = true;
            }
            else
            {
                col++;
                incAfterManager = false;
            }
        }
    }
}
